package webserver;

import java.net.*;
import java.io.*;
import java.util.Properties;

/**
 *
 * @author Daniel
 * @brief Klasse Server
 * Der Server wartete auf eingehennde Verbindungen und 
 */
public class Server {

    private static Server webserver;
    private ServerSocket serverSocket;
    private Socket clientSocket;

    private final static String CONFIG_FILE = "bin/config/jwebserv.config";
    
    private Properties properties = null;
    private int port = 80;
    private String serverName = "";
    private String serverVersion = "";
    private String webDir = "";
    private boolean continueSupportStatus = false;    

    Logging logger = Logging.getInstance();
    

    /**
     * Konstruktor der Klasse Server.
     * Als private da die Klasse als Singelton initialisiert wurde.
     */
    private Server() {
        // nothing to do
    }

    /**
     * Diese Methode prüft, ob schon eine Instanz der Klasse im speicher vorhanden 
     * ist. Wenn ja, wird das bereits vorhandene Objakt zurückgegeben. Falls nein, 
     * wird ein neues Objekt angelegt.
     * @return Speichreradresse des bereits vorhandenen Objekts im Speicher.
     */
    public synchronized static Server getInstance() {
        if (webserver == null)
                webserver = new Server();

        return webserver;
    }

    /**
     * In dieser Methode wird der Config File des Servers gelesen.
     * In diesem werden Eigenschaften wie Port, Servername, Version, 
     * Webserververzeichniss und die Eigenschaft ob 100-Continue 
     * unterstützt wird festgehalten.
     */
    private void loadConfigProperties() {

        properties = new Properties();

        try {
            properties.load(new FileInputStream(CONFIG_FILE));
        }
        catch(Exception ex) {
            logger.log("(ERROR) Could not load config file!!");
            stopServer();
            System.exit(1);
        }

        String prop = properties.getProperty("Port");
        setPort(prop);        

        prop = properties.getProperty("Server");
        setServerName(prop);        

        prop = properties.getProperty("Version");
        setServerVersion(prop);

        prop = properties.getProperty("WebDir");
        setWebDir(prop);

        prop = properties.getProperty("100_Continue");
        setContinueSupportStatus(prop);

    }
    
    /**
     * Festlegen des Ports zur aus Config Datei
     * @param port schreibt den Wert aus Config Datei in die Glob. 
     * Variable Port vom typ Integer.
     */
    private void setPort(String port) {
        this.port = Integer.parseInt(port);
    }
    
    /**
     * Festlegen des Ports aus Glob. Variable Port 
     * @param port die zuvor abgespeicherte Glob. Variable setzen als aktueller Port 
     */
    private void setPort(int port) {
        this.port = port;
    }

    /**
     * Gibt den aktuellen Port zurück
     * @return Aktuell verwendeter Port des Servers.
     */
    public int getPort() {
        return port;
    }

    /**
     * Setzt den aktuellen Servernamen.
     * @param server Setzen des Servernamen aus der Config Datei.
     */
    private void setServerName(String server) {
        serverName = server;
    }

    /**
     *
     * @return Gibt den aktuellen Servernamen zurück.
     */
    public String getServerName() {
        return serverName;
    }

    /**
     *
     * @param version Setzt die aktuelle Versionsnummer aus der Config Datei
     */
    private void setServerVersion(String version) {
        serverVersion = version;
    }

    /**
     *
     * @return Gibt die aktuelle Versionsnummer des Servers zurück.
     */
    public String getServerVersion() {
        return serverVersion;
    }

    /**
     *
     * @param dir Setzt den aktuellen Pfad zu den Verwendeten Webseiten zurück
     */
    private void setWebDir(String dir) {
        webDir = dir;
    }

    /**
     *
     * @return Gibt den aktuellen Lagerort der Inhalte auf der Festplatte zurück
     */
    public String getWebDir() {
        return webDir;
    }

    /**
     *
     * @param property Schaltet den Modus des Webservers (100-Continue) ein oder aus.
     * Abhängig davon, was in der Config Datei steht.
     */
    private void setContinueSupportStatus(String property) {
        if(property.equals("true"))
            continueSupportStatus = true;
        else
            continueSupportStatus = false;
    }

    /**
     *
     * @return Prüft ob 100-Continue ünterstüzt wird oder nicht.
     */
    public boolean getContinueSupportStatus() {
        return continueSupportStatus;
    }

    /**
     * Wartet auf anfragen von Clients und stellt Verbindung mit Clientsocket her.
     * Im Falle einer Verbindung wird ein Objekt der Klasse Connection erzeugt.
     * In dieser wird die Verbindung verwaltet und die Anfrage gelesen
     */

    public void getConnection() {

        while(true) {

            clientSocket = null;            

            try {
                clientSocket = serverSocket.accept();
                new Connection(clientSocket);
                
                logger.log("(INFO) Connection with "+ clientSocket +" established");
            }
            catch(IOException ex) {
                System.out.println("(ERROR) Could not accept client connection!");
            }

        }

    }

    /**
     *     
     * Erzeugt einen neuen Socket
     */
    public void createSocket() {

        serverSocket = null;

        try {
            serverSocket = new ServerSocket(port);
            System.out.println("[Info]: Server has been started successfully!");
            logger.log("#1#");
        } catch (IOException ex) {            
            System.out.println("(ERROR) Could not open port!");

            stopServer();
            
        }

    }

    /**
     * Stoppt den Server
     */
    public synchronized void stopServer() {

        try {
            serverSocket.close();
            logger.log("(INFO): Shutting-down server...");
        } catch (IOException ex) {            
            System.out.println("[Error]: Could not shutting-down server!");
            logger.log("(ERROR) Could not shutting-down server!");
        }
        
    }

    /**
     * Schreibt Daten in Logfile zur Fehlersuche.
     */
    private void printConfigToLogFile() {

        logger.log("---------------CONFIG---------------");
        logger.log("(CONFIG) Server-Port: "+getPort());
        logger.log("(CONFIG) Server-Name: "+getServerName());
        logger.log("(CONFIG) Server-Version: "+getServerVersion());
        logger.log("(CONFIG) WebDir: "+getWebDir());
        logger.log("(CONFIG) 100 Continue: "+getContinueSupportStatus());
        logger.log("------------------------------------");

        

    }


    /**
     * Start des Webservers
     */
    private void startup() {

        loadConfigProperties();

        System.out.println("*******************************************");
        System.out.println("Welcome to "+getServerName()+" (Version: "+ getServerVersion() +")!\nProject created by Frederik, Peter and Daniel :)");
        System.out.println("*******************************************\n");

        createSocket();

        printConfigToLogFile();

        getConnection();
        
    }
    
    /**
     * @param args the command line arguments
     * @brief Erzeugt ein neues Server-Objekt (MAIN METHODE)
     */
    public static void main(String[] args) {

        // boot server
        Server.getInstance().startup();

    }

}